#自写模块
import PCAprocess
import Read_data
#引入模块
import numpy as np 
from sklearn.preprocessing import StandardScaler
from math import sqrt
import matplotlib.pyplot as plt
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Dense
from tensorflow.python.keras.layers import LSTM
import pandas as pd
from pandas import concat
import write_data


'''
LSTM处理模块
输入：特征集（list），标签（list），训练集比例（float）,验证集比例（float），训练轮次epochs，一次训练样本数batch_size
输出：测试集预测数据（list），图像，评估指标
注意：时间序列处理过程第一个数据因NAN丢失
'''
class LSTMProcess :
    def __init__(self,feature,label, placename, train_ratio=0.75,epochs=90,batch_size=80):
        #参数数据
        self.feature=feature
        self.label=label
        self.train_ratio=train_ratio
        self.epochs=epochs
        self.batch_size=batch_size
        self.placename=placename
        #数据集数据
        self.x_train=[]
        self.y_train=[]
        self.x_test=[]
        self.y_test=[]
        self.y_hat_real=[]
        self.y_test_real=[]
        #中间数据
        self.scaler_y=0
        self.len_train_number=0
        self.len_test_number=0

        #方法
        self.mainProcess()


    #main函数
    def mainProcess(self):
        self.feature=np.array(self.feature)
        feature_ndim=self.feature.ndim
        if feature_ndim == 1 :
            feature_list=self.feature.tolist()
            label_list=self.label.tolist()
            LSTM_data = list(zip(feature_list,label_list))
            LSTM_data=[list(c) for c in LSTM_data]
            LSTM_data=np.mat(LSTM_data)
        else :
            self.label=self.label.reshape((-1,1))
            LSTM_data=np.hstack((self.feature,self.label))
        self.data_process(LSTM_data)
        self.lstmProcess()
        self.plot_lstm()
        self.estimate_lstm()




    #数据预处理
    def data_process(self,LSTM_data):
        #特征和标签分别标准化特征
        scaler_x=StandardScaler().fit(LSTM_data[:,:-1])
        scaled_x = scaler_x.transform(LSTM_data[:,:-1])
        self.scaler_y=StandardScaler().fit(LSTM_data[:,-1:])
        scaled_y = self.scaler_y.transform(LSTM_data[:,-1:])
        scaled=np.c_[scaled_x,scaled_y]

        # 构建成监督学习问题(第一行有nan，损失一行)
        reframed = self.series_to_supervised(scaled, 1, 1)
        LSTM_indata = reframed.values
        #计算训练集数量
        n0=len(LSTM_data)-1 #数据的行数，转监督学习时损失一行
        self.len_train_number=int(self.train_ratio*(n0+1))  #向下取整，训练集长度
        self.len_test_number=n0-self.len_train_number #测试集长度

        #分离出训练集和测试集
        n=len(LSTM_indata) #数据的行数
        len_train=int(self.train_ratio*(n+1))  #向下取整，训练集长度
        len_test=n-len_train #测试集长度
        train = LSTM_indata[:len_train,:]
        test = LSTM_indata[len_train:,:]
        #取出最初test集的标签值
        test_real=LSTM_data[len_train:,:]
        self.y_test_real=test_real[:,-1]

        # 分离出特征集与标签
        self.x_train, self.y_train = train[:, :-1], train[:, -1]
        self.x_test, self.y_test = test[:, :-1], test[:, -1]

        # 转换成3维数组 [样本数, 时间步 ,特征数]
        self.x_train = self.x_train.reshape((self.x_train.shape[0], 1, self.x_train.shape[1]))
        self.x_test = self.x_test.reshape((self.x_test.shape[0], 1, self.x_test.shape[1]))

    #lstm训练并预测
    def lstmProcess(self):
        # 创建模型
        model = Sequential() #序贯模型
        model.add(LSTM(50, input_shape=(self.x_train.shape[1], self.x_train.shape[2])))
        model.add(Dense(1))
        model.compile(loss='mae', optimizer='adam')
        # print(model.summary())
        # 拟合网络
        history = model.fit(self.x_train, self.y_train, epochs=self.epochs, batch_size=self.batch_size, verbose=0, shuffle=False)
        # 对测试集进行预测
        yhat = model.predict(self.x_test)
        self.y_hat_real=self.scaler_y

        #标准化后复原
        self.y_hat_real=self.scaler_y.inverse_transform(yhat)

    """
    获取绘图数据函数
    输入：两个一维list要按列并列写入文件，一个文件名
    输出：一个xls文件
    """
    def plotData(self,list1,list2,filename):
        plot_list1=np.array(list1).reshape((-1,1))
        plot_list2=np.array(list2).reshape((-1,1))
        plot_data=np.hstack((plot_list1,plot_list2))
        write_data.write_data(filename,plot_data)

    #画图
    def plot_lstm(self):
        self.y_test_real=self.y_test_real.tolist()
        self.y_test_real=self.y_test_real[1:]
        r = len(self.y_test_real) + 1
        #写出绘图数据
        self.plotData(self.y_hat_real,self.y_test_real,'PredictionResult/'+self.placename+'LSTM_预测残差_真实残差.xls')

        plt.figure(figsize=(20,16)) #创建画布。大小为10*10，像素为160
        plt.style.use('dark_background') #黑色背景
        plt.plot(np.arange(1,r), self.y_hat_real, 'r-', label="预测客流")
        plt.plot(np.arange(1,r), self.y_test_real, '-', label="真实客流")
        plt.title('LSTM模型预测客流与真实客流对比图') #添加标题
        plt.ylabel('客流量') #添加y坐标
        plt.rcParams['lines.linewidth']=2 #修改线条宽度
        plt.rcParams['axes.spines.top']=False #去除顶部轴
        plt.rcParams['axes.spines.right']=False #去除右侧轴
        plt.rcParams['font.size']=26 # 设置字体大小
        plt.legend(loc='upper right')
        
        plt.savefig('PredictionResult/'+self.placename+'真实客流与预测客流对比折线图（LSTM模型）.png')
        plt.show()

    #评估指标
    def estimate_lstm(self):
        # 对线性核函数模型评估
        print("LSTM决定系数(r方，R_squared)值为：", r2_score(self.y_test_real,self.y_hat_real))
        # 计算RMSE
        rmse = sqrt(mean_squared_error(self.y_test_real, self.y_hat_real))
        print('LSTM均方根误差(RMSE,Root Mean Square Error): ',rmse)
        print("LSTM均方误差(MSE, mean squared error)为:", mean_squared_error(self.y_test_real,self.y_hat_real))
        print("LSTM平均绝对值误差(MAE,mean_absolute_error)为:", mean_absolute_error(self.y_test_real,self.y_hat_real))
        return r2_score(self.y_test_real,self.y_hat_real)



    # 将序列转换成监督学习问题
    # data:观测序列。格式是一个list或2维Numpy Array
    # n_in:观测数据input(X)的步长，范围[1,len(data)]，默认为1
    # n_out:观测数据output(y)的步长，范围[0,len(data)-1 ]，默认为1
    # dropnan:是否删除存在NaN的行，默认为True
    def series_to_supervised(self,data, n_in=1, n_out=1, dropnan=True):
        n_vars = 1 if type(data) is list else data.shape[1]
        df = pd.DataFrame(data)
        cols, names = list(), list()
        # 输入序列(t-n, ... t-1)
        for i in range(n_in, 0, -1):
            cols.append(df.shift(i))
            names += [('var%d(t-%d)' % (j+1, i)) for j in range(n_vars)]
        # 预测序列 (t, t+1, ... t+n)
        for i in range(0, n_out):
            cols.append(df.shift(-i))
            if i == 0:
                names += [('var%d(t)' % (j+1)) for j in range(n_vars)]
            else:
                names += [('var%d(t+%d)' % (j+1, i)) for j in range(n_vars)]
        # 将他们整合在一起
        agg = concat(cols, axis=1)
        agg.columns = names
        # print(agg)
        # 删除那些包含空值(NaN)的行
        if dropnan:
            agg.dropna(inplace=True)
        return agg

# #联调测试
# rd_feature=Read_data.Read_data("data_siguniangshan.xlsx",1,13)#取第1列到第12列数据(list)
# feature_data1=rd_feature.outputdata

# pcaP=PCAprocess.PCAprocess(feature_data1,3)#PCA处理数据
# feature_data1=pcaP.output_data

# rd_feature2=Read_data.Read_data("data_siguniangshan.xlsx",14,22)#取第14列到第22列数据(list)
# feature_data2=rd_feature2.outputdata

# feature_data =np.hstack((feature_data1,feature_data2))#合并特征集

# rd_label=Read_data.Read_data("data_siguniangshan.xlsx",23,23)#取第23列数据客流
# label_data=rd_label.outputdata
# LSTMProcess(feature_data,label_data,0.5)#svr处理数据